home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HPAVC
/
HPAVC CD-ROM.iso
/
IPX2.ZIP
/
ipx.doc
Wrap
Internet Message Format
|
1994-10-03
|
23KB
From: s921878@minyos.xx.rmit.EDU.AU (Daniel John Lee Parnell)
Subject: Re: IPX document in x2ftp
To: jon@stekt.oulu.fi
Date: Mon, 3 Oct 1994 10:34:04 +1000 (EST)
Content-Type: text/plain; charset=US-ASCII
Content-Length: 23154
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
IIIIII PPPPPP XX XX
II PP PP XX XX
II PP PP XX XX
II PP PP XX XX
II PP PP XX XX
II PPPPPP XXX
II PP XX XX
II PP XX XX
II PP XX XX How to send IPX packets over a
II PP XX XX network by Daniel Parnell
IIIIII PP XX XX s921878@minyos.xx.rmit.oz.au
daniel@bunyip.ph.rmit.oz.au
Started : 29th March 1994
Last Mod: 2nd September 1994
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
On March 24th 1994 I managed to send my first IPX packet. This was a very
exciting moment for me as it opened up a vast number of applications that
were before closed to me. NETWORKED GAMES!!! :) This document attempts
to guide the would be IPXer in the techniques of sending IPX packets.
Admittedly, it has only been 5 days since my first sucessful packet, but in
that short time I have developed a set of routines that seem to work quite
well. :) These routines are included in appendix A and an example program
is included in appendix B. These routines are written in Turbo Pascal 6.0,
but should be reasonably portable to other languages.
First of all, a little background. IPX is a protocol that allows packets
of information to be sent over a network. These packets can be sent from one
machine to another or sent to all machines (nodes). Sending a packet to all
machines is called a BROADCAST. A broadcast will usually be confined to just
the local network, however there are ways around this which I will not go
into here. ;) These packets have a specific structure which is shown in
figure 1.
Figure 1 - The IPX header
------------------------------------------------------------------------
| Name | Size (bytes) | Description |
----------------|--------------|---------------------------------------|
|Check | 2 bytes | Bigendian check sum |
|Length | 2 bytes | Bigendian length of packet |
|tc | 1 byte | Transport control (age of packet) |
|pType | 1 byte | Packet type (always use 0) |
|dest | 12 bytes | Address of destination node |
|src | 12 bytes | Address of source node |
----------------|--------------|---------------------------------------|
By the way, bigendian means that the first byte holds the most significant
value, which is the opposite of the normal way of doing things on the PC.
For reasons better known to Novell, the checksum is always set to $ffff.
The length field contains the total length of the packet, including the IPX
header. Transport control keeps a count of the number of routers a packet
has been through. The format of the address is shown in Figure 2.
Figure 2 - The net address field
------------------------------------------------------------------------
| Name | Size (bytes) | Description |
----------------|--------------|---------------------------------------|
|network | 4 bytes | Network address |
|node | 6 bytes | Node address |
|socket | 2 byte | Socket number |
----------------|--------------|---------------------------------------|
Sockets are a device that lets a node decide if it will act on a packet.
It allows multiple programs on the one PC to use IPX packets completely
transparent to the others. It also means that a broadcasted packet will only
be received by a machine if it has a socket open for that which the packet is
addressed. So packets can be ignored by nodes that are not capable of
accepting them. Special care must be taken to ensure that no two programs try
to send different types of packets to the same socket!
The next order of bussiness is to tell the PC that we want to open a socket.
This is accomplished by creating an Event Control Block (ECB). Figure 3 shows
the format of the ECB.
Figure 3 - Format of the ECB
------------------------------------------------------------------------
| Name | Size (bytes) | Description |
----------------|--------------|---------------------------------------|
|Link | 4 bytes | Pointer to next ECB? |
|ESR | 4 bytes | Pointer to Event Service Routine |
|InUse | 1 byte | Flag telling the ECB status |
|complete | 1 byte | Flag telling the ECB completion code |
|socket | 2 bytes | Bigendian socket number for ECB |
|IPXwork | 4 bytes | Work space for IPX |
|Dwork | 12 bytes | Work space for driver |
|immedAddr | 12 bytes | Address to send to. |
|fragCount | 2 bytes | Number of fragments |
|FragData | 4 bytes | Pointer to data fragment |
|FragSize | 2 bytes | Size of data fragment |
----------------|--------------|---------------------------------------|
All the fields in the ECB should be set to zero before initializing the ECB.
This makes the code to initialize the ECB much nicer! No ESR is used in my
routines which means the the program must constantly ckeck the ECB to see if
a packet has arrived. I don't see this as any great drawback however.
To send a broadcast, set the node address to all $ff. This is done
automatically by my routines.
FragData must be initialized to point to the IPX header and the data we want
to send MUST follow directly after this header. An example of this can be
seen in Appendix B in the definition of the record type "Packet". The ECB
need not be included in the packet record, but for simplicity I chose to do
so.
The procedures InitSendPacket and InitReceivePacket give examples of the
initialization of both the ECB and IPX header.
Once all of the fields are initialized, it is necessary to tell the IPX
driver about our ECB. This is done by first opening a socket to use.
Interrupt $7A is assigned to the Novell API, and is the old way of accessing
the routines. It is however a much easier to use than the recommended method
(in my opinion anyway). The sub-functions that we use are given in Table 1.
Table 1
-----------------------------------------------------------------------------
|Function|Name |Regs |
-----------------------------------------------------------------------------
| $0000 |Open Socket |AL=Longevity DX=Socket number (bigendian) |
| | |Returns:AL=Error code DX=Socket Number (bigendian) |
-----------------------------------------------------------------------------
| $0001 |Close Socket|DX=Socket number (bigendian) |
-----------------------------------------------------------------------------
| $0003 |Send Packet |ES=Seg of ECB SI=Offset of ECB |
-----------------------------------------------------------------------------
| $0004 |Listen for |ES=Seg of ECB SI=Offset of ECB |
| |packet |Returns:AL=Error code |
-----------------------------------------------------------------------------
| $0009 |Get Local |ES=Seg of address array SI=Offset of address array |
| |Address | |
-----------------------------------------------------------------------------
| $000A |Im Idle |None |
-----------------------------------------------------------------------------
NOTE: The function number is stored in BX.
After attempting to open a socket it is advisable to test if IPX is
present. This is done by calling int $2F with AX=$7F00. If on return AL
does not contain $FF, then IPX is not installed. This is also a good point
to get your local address as your program should only have to do this once.
See the procedure InitIPX in appendix A for an example of this.
Once we have an open socket and some set up ECBs and IPX headers, we can
tell the IPX driver about them. First we will look at a recieving ECB and
IPX header, followed by a sending.
For a recieve record, the IPX header does not need to be initialized at all.
However, I think it is a good idea to zero all the fields just be on the safe
side. The ECB however, does need to have several fields set. The inUse flag
should be set to $1D (don't ask me why, actually it works without this, but
several of the recources I consulted did this). The socket number must be
initialized, and remember, this is bigendian so be careful! Finally the
fragCount, fragData and fragSize fields must be set. All the other fields
should be set to zero!
Once all these fields are set, call "Listen For Packet" to tell the IPX
driver about the ECB. This will return immediatly, but if any packets arrive
for that socket they will be placed into the record. To see if a packet has
arrived, it is necessary to check the InUse flag in the ECB. If this is $00,
then a packet has arrived and needs to be processed before another can be
accepted. If a packet arrives when there is an unprocessed packet, then it
will be lost. This means that a packet should be processed as soon as
possible. The record can be released by simply calling "Listen For Packet"
again.
OK, we now have a recieve record waiting for a packet to arrive. Now we
need a send record! A send record needs all of the same fields as a receive
record, but on top of that it also needs the IPX header to be filled in.
The checksum is set to $ffff and the packet type is set to $00 (this is a
packet exchange packet). Next the destination address is set. For a
broadcast you should set the net address to your local net address, the node
sould be the broadcast node ($ff,$ff,$ff,$ff,$ff,$ff) and the socket should be
set up. Your program should set up the source address with the info from the
localAddr record, and the source socket can be anything, but again, for
simplicity I usually set it to the same value as the destination socket.
We then put some data into the data portion of the record and call "Send
Packet". The packet will then be sent to the destination node on the network.
If the node field is set to the broadcast address, the packet will be sent to
all nodes on the network, including the source node.
As a point of interest, you should call "Im idle" whenever your program
isn't doing anything an particular to allow the IPX driver to run more
quickly.
So there you have it. Hopefully you should now be able to send and receive
IPX packets on a network. Have fun!
Daniel Parnell
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
A PPPPP PPPPP EEEEEE N N DDDDDD IIIII X X A
A A P P P P E NN N D D I X X A A
A A P P P P E N N N D D I X X A A
A A P P P P E N N N D D I X X A A
A A P P P P E N N N D D I X X A A
AAAAAAA PPPPP PPPPP EEEE N N N D D I X AAAAAAA
A A P P E N N N D D I X X A A
A A P P E N NN D D I X X A A
A A P P E N N D D I X X A A
A A P P E N N D D I X X A A
A A P P EEEEEE N N DDDDDD IIIII X X A A
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
{ IPX 1.0 By Daniel Parnell 22 March 1994 s921878@minyos.xx.rmit.oz.au
This unit allows the sending of IPX packets over a Novell network
Use it if you want, but please give me some credit for it :)
See the other file which is a chat program for info on how to use this unit
}
unit ipx;
interface
uses dos;
type
netAddr = array[1..4] of byte; { The address of a network }
nodeAddr = array[1..6] of byte; { The address of a node in a network }
address = array[0..1] of word; { A pointer to the data 0=offset 1=seg }
netAddress = record
Net : netAddr; { network address }
Node : nodeAddr; { node address }
Socket : word; { Big endian socket number}
end;
localAddrT = record
Net : netAddr; { my network address }
Node : nodeAddr; { my node address }
end;
ECBType = record
link : address; { Pointer to next ECB? }
ESR : address; { Event Service Routine 00000000h if none }
inUse : byte; { In use flag }
complete : byte; { Completeing flag }
socket : word; { Big endian socket number }
IPXwork : array[1..4] of byte; { IPX work space }
Dwork : array[1..12] of byte; { Driver work space }
immedAddr : nodeAddr; { Immediate local node address }
fragCount : word; { Fragment count }
fragData : address; { Pointer to data fragment }
fragSize : word; { Size of data fragment }
end;
IPXheader = record
check : word; { big endian checksum }
length : word; { big endian length in bytes }
tc : byte; { transport control }
pType : byte; { packet type }
dest : netAddress; { destination network address }
src : netAddress; { source network address }
end;
const
MYSOCKET : word = $869C; { This is the DOOM official socket number }
BROADCAST : nodeAddr = ($ff,$ff,$ff,$ff,$ff,$ff); { Address for broadcast }
var
localAddr : localAddrT;
function IPXopenSocket(longevity : byte; var socketNumber : word):byte;
procedure IPXcloseSocket(socketNumber : word);
procedure GetLocalAddress;
procedure IPXsendPacket(var E : ECBtype);
function IPXlistenForPacket(var E : ECBtype):byte;
procedure ImIdle;
procedure InitSendPacket(var ecb : ecbType; var ipx : ipxHeader; size,sock : word);
procedure InitReceivePacket(var ecb : ecbType; var ipx : ipxHeader; size,sock : word);
procedure InitIPX;
implementation
{ Open a socket
PARAMS: longevity = $00 for open till close or terminate
= $ff for open till close use for TSR
socketNumber = 0 for dynamic allocation
= anything else
RETURNS: completion code $00 = success
$fe = socket table full
$ff = socket already open }
function IPXopenSocket(longevity : byte; var socketNumber : word):byte;
var
regs : registers;
begin
regs.bx:=$0000;
regs.al:=longevity;
regs.dx:=swap(socketNumber);
intr($7A,regs);
if socketNumber=$0000 then
socketNumber:=swap(regs.dx);
IPXopenSocket:=regs.al;
end;
{ Close a socket
PARMS: socketNumber = a socket to close }
procedure IPXcloseSocket(socketNumber : word);
var
regs : registers;
begin
regs.bx:=$0001;
regs.dx:=swap(socketNumber);
intr($7A,regs);
end;
{ Get my address and put it into the local address array! }
procedure GetLocalAddress;
var
regs : registers;
begin
regs.bx:=$0009;
regs.es:=seg(localAddr);
regs.si:=ofs(localAddr);
intr($7A,regs);
end;
{ Send an IPX packet
PARAMS: var E = an initialized Event Control Block }
procedure IPXsendPacket(var E : ECBtype);
var
regs : registers;
begin
regs.bx:=$0003;
regs.es:=seg(E);
regs.SI:=ofs(E);
intr($7A,regs);
end;
{ Listen for an IPX packet
PARAMS: var E = an initialize Event Control Block
RETURNS: 0 for OK, nonzero for an error ????}
function IPXlistenForPacket(var E : ECBtype):byte;
var
regs : registers;
begin
regs.bx:=$0004;
regs.es:=seg(E);
regs.SI:=ofs(E);
intr($7A,regs);
IPXlistenForPacket:=regs.al;
end;
{ Tell the IPX driver that we aren't doing anything at the moment }
procedure ImIdle;
var
regs : registers;
begin
regs.bx:=$000A;
intr($7A,regs);
end;
{ Set up the fields in a send IPX record }
procedure InitSendPacket(var ecb : ecbType; var ipx : ipxHeader; size,sock : word);
begin
fillChar(ecb,sizeOf(ecb),#0);
fillChar(ipx,sizeOf(ipx),#0);
with ecb do
begin
socket:=swap(sock); { Big endian socket number }
fragCount:=1; { Fragment count }
fragData[0]:=ofs(IPX); { Pointer to data fragment }
fragData[1]:=seg(IPX);
fragSize:=sizeof(IPX)+size; { Size of data fragment }
immedAddr:=BROADCAST; { Needs to be BROADCAST?? }
end;
with ipx do
begin
check:=$ffff; { NO CHECKSUM }
ptype:=0; { Packet exchange packet }
dest.net:=localAddr.net; { Send to this network }
dest.node:=BROADCAST; { Send to everybody! }
dest.socket:=swap(sock); { Send to my socket }
src.net:=localAddr.net; { From this net }
src.node:=localAddr.node; { From ME }
src.socket:=swap(sock); { From my socket }
end;
end;
{ Set up the fields in a recieve IPX record }
procedure InitReceivePacket(var ecb : ecbType; var ipx : ipxHeader; size,sock : word);
begin
fillChar(ecb,sizeOf(ecb),#0);
fillChar(ipx,sizeOf(ipx),#0);
with ecb do
begin
inUse:=$1d; { ???? }
socket:=swap(sock); { Big endian socket number }
fragCount:=1; { Fragment count }
fragData[0]:=ofs(IPX); { Pointer to data fragment }
fragData[1]:=seg(IPX);
fragSize:=sizeof(IPX)+size; { Size of data fragment }
end;
if IPXlistenForPacket(ecb)<>0 then ; { Tell IPX to listen }
end;
{ Set up IPX and get the local address }
procedure InitIPX;
var
i : integer;
regs : registers;
begin
regs.ax:=$7A00;
intr($2f,regs);
if regs.al<>255 then
begin
writeln('ERROR WHILE INITIALIZING IPX!');
halt(1);
end;
getLocalAddress;
end;
begin
end.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
A PPPPP PPPPP EEEEEE N N DDDDDD IIIII X X BBBBBB
A A P P P P E NN N D D I X X B B
A A P P P P E N N N D D I X X B B
A A P P P P E N N N D D I X X B B
A A P P P P E N N N D D I X X B B
AAAAAAA PPPPP PPPPP EEEE N N N D D I X BBBBBB
A A P P E N N N D D I X X B B
A A P P E N NN D D I X X B B
A A P P E N N D D I X X B B
A A P P E N N D D I X X B B
A A P P EEEEEE N N DDDDDD IIIII X X BBBBBB
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
program CHAT;
uses CRT,IPX;
type
Packet = record
ecb : ECBType;
IPX : IPXheader;
data : string;
end;
var
send,receive : Packet;
procedure Main;
var
line : string;
y : integer;
done : boolean;
k : char;
i : integer;
handle : string;
begin
ClrScr;
writeln('CHATER BOX v0.5 By Daniel Parnell 25th March 1994');
writeln;
write('Enter your handle :');
readln(handle);
window(1,1,80,23);
textBackground(Blue);
textColor(Yellow);
clrScr;
window(1,24,80,25);
textBackground(Red);
textColor(Yellow);
clrScr;
y:=1;
line:='';
done:=FALSE;
repeat
repeat
until KeyPressed or (receive.ecb.inuse=0);
if receive.ecb.inuse=0 then
begin
window(1,1,80,23);
gotoXY(1,y);
textBackground(Blue);
textColor(Yellow);
writeln(receive.data);
y:=WhereY;
if IPXlistenForPacket(receive.ecb)<>0 then
begin
writeln(#7,'ERROR TRYING TO receive A PACKET!');
halt(2);
end;
window(1,24,80,25);
GotoXY(1,length(line)+1);
end;
if KeyPressed then
begin
k:=ReadKey;
case k of
#13 : if line<>'' then
begin
send.data:='<<'+handle+'>>'+line;
with send.ecb do
for i:=1 to 6 do
ImmedAddr[i]:=$ff;
repeat
until send.ecb.inuse=0;
IPXsendPacket(send.ecb);
line:='';
end;
#8 : if length(line)>0 then
line:=copy(line,1,length(line)-1);
#0 : k:=ReadKey;
#27 : done:=TRUE;
else
if length(line)<79 then
line:=line+k
else
begin
sound(1000);
delay(100);
noSound;
end;
end;
window(1,24,80,25);
textBackground(Red);
textColor(Yellow);
GotoXY(1,1); clreol; write(line);
end;
until done;
end;
begin
if IPXopenSocket(0,MYSOCKET)=0 then
begin
InitIPX;
with send do
InitSendPacket(ecb,ipx,sizeof(String),MYSOCKET);
with receive do
InitReceivePacket(ecb,ipx,sizeof(String),MYSOCKET);
Main;
IPXcloseSocket(MYSOCKET);
end;
TextColor(LightGray);
TextBackground(Black);
window(1,1,80,25);
clrScr;
end.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Daniel Parnell - Email to s921878@minyos.xx.rmit.oz.au - AMIGA 500&600 1 MEG
Second Year Applied Physics student at R.M.I.T. Melbourne Australia. *:|()
People who drink petrol shouldn't smoke. AMOS 1.36 with Compiler and 3D
Squaxx Dek Thargo from prog 579 - C64 Amiga Mac CCPM 6502 8086 - Forth